import java.util.HashMap;

/**
 * Data structure represent a call frame in jstack output 
 */
public class JCallFrame
{
    public enum FrameType
    {
        JStackFrame,  // e.g. at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
        SAFrame       // e.g. - java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=135 (Interpreted frame)
    }

    /**
     * Add a child frame to the current frame
     * @param jCallFrameName
     * @return
     */
    public JCallFrame addChildFromText(String frameText, FrameType frameType)
    {
        JCallFrame childFrame = addChild(frameText);
        int beginOfSourceName = frameText.indexOf('(');
        int beginOfLineNumber = frameText.indexOf(':');
        int endOfLineNumber = frameText.indexOf(')');
        String childLineNumber = "0";
        String childFileName = "unknown";
        String childFunctionName = frameText;
        switch(frameType)
        {
        case JStackFrame:
                beginOfSourceName = frameText.indexOf('(');
                beginOfLineNumber = frameText.indexOf(':');
                endOfLineNumber = frameText.indexOf(')');
                if(beginOfSourceName > 0)
                {
                    if(beginOfLineNumber > 0)
                    {
                        childLineNumber = frameText.substring(beginOfLineNumber + 1, endOfLineNumber);
                        childFileName = frameText.substring(beginOfSourceName + 1, beginOfLineNumber);
                    }
                    else
                    {
                        childFileName = frameText.substring(beginOfSourceName + 1, endOfLineNumber);
                    }
                    childFunctionName = frameText.substring(0, beginOfSourceName);
                }
            break;
        case SAFrame:
                String[] sections = frameText.split("@bci=[0-9]+");
                childFunctionName = sections[0].trim();
                if(sections.length > 1 && sections[1].contains("line="))
                {
                    childLineNumber = sections[1].split("line=")[1].split(" ")[0];
                }
                int classNameEnd = childFunctionName.indexOf('$');
                if(classNameEnd < 0) 
                {
                    classNameEnd = childFunctionName.lastIndexOf('.', childFunctionName.indexOf('('));
                }
                int classNameBegin = childFunctionName.lastIndexOf('.', classNameEnd - 1);
                if(classNameBegin < 0)
                {
                    classNameBegin = 0; 
                }
                else
                {
                    classNameBegin = classNameBegin + 1;
                }

                if(classNameEnd <= classNameBegin)
                {
                    childFileName = "unknown";
                }
                else
                {
                    childFileName = childFunctionName.substring(classNameBegin, classNameEnd) + ".java";
                }
            break;
        default:
            throw new IllegalArgumentException("bad frameText :" + frameText);
        }
        childFrame.setCurrentLineNumber(childLineNumber);
        childFrame.setSourceFileName(childFileName);
        childFrame.setCurrentFunctionName(childFunctionName);
        return childFrame;
    }
    public JCallFrame addChild(String jCallFrameName)
    {
        if(fChildren.containsKey(jCallFrameName))
        {
            fChildren.get(jCallFrameName).fCount++;
        }
        else
        {
            JCallFrame newCallFrame = new JCallFrame();
            fChildren.put(jCallFrameName, newCallFrame);
            newCallFrame.fCount++;
        }
        return fChildren.get(jCallFrameName);
    }     
    public int getfCount()
    {
        return fCount;
    }
    
    public HashMap<String, JCallFrame> getfChildren()
    {
        return fChildren;
    }

    public String getSourceFileName()
    {
        return sourceFileName;
    }
    public void setSourceFileName(String sourceFileName)
    {
        this.sourceFileName = sourceFileName;
    }
    public String getCurrentLineNumber()
    {
        return currentLineNumber;
    }
    public void setCurrentLineNumber(String currentLineNumber)
    {
        this.currentLineNumber = currentLineNumber;
    }
    public String getCurrentFunctionName()
    {
        return currentFunctionName;
    }
    public void setCurrentFunctionName(String currentFunctionName)
    {
        this.currentFunctionName = currentFunctionName;
    }
    private int fCount = 0;
    private HashMap<String, JCallFrame> fChildren = new HashMap<String, JCallFrame>();
    private String sourceFileName = "unknown";
    private String currentLineNumber = "0";
    private String currentFunctionName = "unknown";
}